home *** CD-ROM | disk | FTP | other *** search
- // TCPCFSocket.m
- // Maxter
- //
- // Created by bgg on Sat Nov 25 2000.
- // Copyright (c) 2000 Object Craft P/L. All rights reserved.
-
- #import "TCPCFSocket.h"
-
- RCS_ID("$Id: TCPCFSocket.m,v 1.13 2000/12/08 05:47:40 bgg Exp $");
-
- // Based on work from Douglas Johnston <doug@timeinc.net>. Understanding the
- // CoreFoundation call backs was due to his efforts, bravo!
-
- static void _tcpCFSocketCallback(CFSocketRef s, CFSocketCallBackType type,
- CFDataRef address, const void *data,
- void *info);
- const void *_tcpCFSocketRetainContext(const void *);
- void _tcpCFReleaseSocketContext(const void *);
-
-
- @implementation TCPCFSocket
-
- - (void)dealloc
- {
- if (_runLoopSource)
- [self removeFromRunLoop];
- [self _invalidateSocket];
- [super dealloc];
- }
-
-
- // action methods
-
- - (void)dataAvailableWithNotification;
- {
- [self _addToRunLoopWithCallBackType:kCFSocketReadCallBack];
- }
-
- - (void)acceptWithNotification
- {
- [self _addToRunLoopWithCallBackType:kCFSocketDataCallBack];
- }
-
- - (void)readWithNotification
- {
- [self _addToRunLoopWithCallBackType:kCFSocketDataCallBack];
- }
-
- // need to overload acceptConnection to close the socket fd held open
- // by the CFSocket
-
- - (void)acceptConnection
- {
- [super acceptConnection];
- [self _invalidateSocket];
- }
-
- // XXX add more? -writeWithNotification, etc ...?
-
- // remove our listener from the run loop.
-
- - (void)removeFromRunLoop
- {
- if (_runLoopSource == NULL)
- return;
- CFRunLoopRemoveSource([[NSRunLoop currentRunLoop] getCFRunLoop],
- _runLoopSource,
- kCFRunLoopDefaultMode);
- if (CFRunLoopSourceIsValid(_runLoopSource))
- CFRunLoopSourceInvalidate(_runLoopSource);
- CFRelease(_runLoopSource);
- _runLoopSource = NULL;
- }
-
- // internal methods
-
- // closes file descriptor in CFSocket
-
- - (void)_invalidateSocket
- {
- if (_cfSocketRef != NULL) {
- if (CFSocketIsValid(_cfSocketRef))
- CFSocketInvalidate(_cfSocketRef);
- CFRelease(_cfSocketRef);
- _cfSocketRef = NULL;
- }
- }
-
- // Add the CFSocket to the run loop this way:
-
- - (void)_addToRunLoopWithCallBackType:(CFSocketCallBackType)callBackType
- {
- _socketContext.version = 1;
- _socketContext.retain = &_tcpCFSocketRetainContext;
- _socketContext.release = &_tcpCFReleaseSocketContext;
- _socketContext.copyDescription = NULL;
- _socketContext.info = self; // arg passed to call back
-
- _cfSocketRef = CFSocketCreateWithNative(NULL, [self socketFD], callBackType,
- &_tcpCFSocketCallback, &_socketContext);
- if (_runLoopSource != NULL) {
- [NSException raise:TCPCFSocketException
- format:@"_addToRunLoop:run loop active"];
- }
- _runLoopSource = CFSocketCreateRunLoopSource(NULL, _cfSocketRef, 0);
- CFRunLoopAddSource([[NSRunLoop currentRunLoop] getCFRunLoop],
- _runLoopSource,
- kCFRunLoopDefaultMode);
- }
-
- // generate appropriate notification to alert caller that event has been
- // recieved.
-
- - (void)_processCallBackType:(CFSocketCallBackType)type withData:(NSData *)data
- {
- NSDictionary *ui;
- ONTCPSocket *newSocket;
- NSNotificationCenter *defCenter = [NSNotificationCenter defaultCenter];
-
- switch (type) {
- case kCFSocketNoCallBack: // no call back (how are we here?!)
- [NSException raise:TCPCFSocketArgumentException format:@"impossible call back"];
- break;
- case kCFSocketReadCallBack: // data available or ready to accept
- [self removeFromRunLoop];
- [defCenter postNotificationName:TCPCFSocketDataAvailableNotification
- object:self];
- break;
- case kCFSocketAcceptCallBack: // acception connected, new fd in data
- // XXX this is RONG!
- [NSException raise:TCPCFSocketException format:@"XXX not implemented (yet)"];
- // newSocket = [ONTCPSocket socketWithFD:*(const int *)[data bytes]];
- newSocket = nil;
- ui = [NSDictionary dictionaryWithObjectsAndKeys:
- newSocket, TCPCFSocketNotificationONTCPSocketItem,
- nil];
- [defCenter postNotificationName:TCPCFSocketAcceptedNotification
- object:self
- userInfo:ui];
- break;
- case kCFSocketDataCallBack: // data read from socket
- ui = [NSDictionary dictionaryWithObjectsAndKeys:
- data, TCPCFSocketNotificationDataItem,
- nil];
- [defCenter postNotificationName:TCPCFSocketReadCompletionNotification
- object:self
- userInfo:ui];
- break;
- default:
- [NSException raise:TCPCFSocketArgumentException format:@"unknown CFSocket call back type: %d", type];
- break;
- }
- }
- @end
-
-
- // call-back function from CFRunLoop to handle socket activity. We convert
- // the passed object into an NSData and then deal with those details by
- // calling a method in the instance of TCPCFSocket which requested this
- // callback; the instance's address is passed as "info".
- //
- // I suspect that data already is an "NSData *" but anyway we can't tell that
- // from here. Oh well.
-
- static void
- _tcpCFSocketCallback(CFSocketRef s, CFSocketCallBackType type, CFDataRef address,
- const void *data, void *info)
- {
- NSData *dataObj;
- char *buf;
-
- if (data != NULL) {
- if ((buf = NSZoneMalloc(NSDefaultMallocZone(), CFDataGetLength(data))) == 0)
- [NSException raise:NSMallocException format:@"_tcpCFSocketCallback"];
- CFDataGetBytes(data, CFRangeMake(0, CFDataGetLength(data)), buf);
- dataObj = [NSData dataWithBytesNoCopy:buf length:CFDataGetLength(data)];
- } else
- dataObj = nil;
-
- [(TCPCFSocket *)info _processCallBackType:type withData:dataObj];
- }
-
- const void *
- _tcpCFSocketRetainContext(const void *info)
- {
- return info;
- }
-
- void
- _tcpCFReleaseSocketContext(const void *info)
- {
- // (do nothing)
- }
-
- NSString *TCPCFSocketException = @"TCPCFSocketException";
- NSString *TCPCFSocketArgumentException = @"TCPCFSocketArgumentException";
- NSString *TCPCFSocketDataAvailableNotification = @"TCPCFSocketDataAvailableNotification";
- NSString *TCPCFSocketAcceptedNotification = @"TCPCFSocketAcceptedNotification";
- NSString *TCPCFSocketReadCompletionNotification = @"TCPCFSocketReadCompletionNotification";
- NSString *TCPCFSocketNotificationONTCPSocketItem = @"TCPCFSocketNotificationONTCPSocketItem";
- NSString *TCPCFSocketNotificationDataItem = @"TCPCFSocketNotificationDataItem";